home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Libraries / SAT 2.4.0 / SAT / Add-ons / Storage / Scores.p < prev    next >
Encoding:
Text File  |  1997-03-05  |  13.9 KB  |  548 lines  |  [TEXT/PJMM]

  1. {================================================}
  2. {============= Score handling and display ==============}
  3. {================================================}
  4.  
  5. {Reusable score and highscore unit! All code that needs changing is in ScoresStubs.}
  6.  
  7. {You should call InitScores before using any other routines, and pass the refnum of your}
  8. {preference file. If you don't, it will still work, but the application file (or whatever the}
  9. {current resource file is when you call) will be used - a "best effort" soluition that may}
  10. {not be what you want.}
  11.  
  12. unit Scores;
  13.  
  14. interface
  15.     uses
  16. {$IFC UNDEFINED THINK_PASCAL}
  17.         Types, QuickDraw, Fonts, Events, Packages, Menus, Dialogs, Windows,{}
  18.         OSUtils, ToolUtils, Resources, Controls, QuickDrawText, TextUtils, {}
  19.         Memory, 
  20. {$ELSEC}
  21.         InterfacesUI, 
  22. {$ENDC}
  23.         SAT, ScoresStubs;
  24.  
  25.     procedure InitScores (prefFile: Integer);                    { Loads the high score list and the high score window. }
  26.     procedure ZeroScore;                     { Call this on New Game! }
  27.     procedure AddScore (amount: Longint);
  28.     procedure AddScoreImmediate (amount: Longint);
  29.  
  30.     procedure DrawHighScores (bounds: Rect; rankPos, namePos, datePos, levelPos: Integer; markLatest: Boolean);
  31.     procedure EraseHighScores (ask: Boolean);
  32.  
  33. {Check if the current score makes the list, and if it does, fire up a modal dialog and write the result into}
  34. {the high score list.}
  35.     function TestNewHigh (level: Integer): Boolean;
  36.  
  37. {IsNewHigh and SetNewHigh should be used if you don't want the default modal dialog, but rather}
  38. {get the player name some other way.}
  39.  
  40. {Checks if the current score is high enough for the high score list.}
  41.     function IsNewHigh: Boolean;
  42. {After IsNewHigh has returned true, and you have got a name, call SetNewHigh to write the}
  43. {score into the high score list.}
  44.     procedure SetNewHigh (name: Str255; level: Integer);
  45.  
  46.  
  47. implementation
  48.  
  49. { Highscore record }
  50.     type
  51.         str20 = string[kStringSize];
  52.  
  53.         HsRec = record
  54.                 highScore: array[1..kListLength] of longint;
  55.                 highPlayer: array[1..kListLength] of Str20;
  56.                 lastPlayer: Str20;
  57.                 when: array[1..kListLength] of Longint;
  58.                 level: array[1..kListLength] of Integer;
  59.             end;
  60.         HsPtr = ^HsRec;
  61.         HsHnd = ^HsPtr;
  62.  
  63.     var
  64.         hs: hsHnd;                {Handle to high scores resource, initialized by }
  65.         gLastHigh: Integer;    {Index of last high score}
  66.         scoresInitialized: Boolean;
  67.         gScore: Longint;
  68.         gPrefFile: Integer;    {Pref file}
  69.  
  70. {Standard filter function, here used for AskHigh}
  71.  
  72.     function StdFilter (theDialog: DialogPtr; var theEvent: EventRecord; var itemHit: integer): boolean;
  73.         var
  74.             theChar: Char;
  75.             kind: integer;
  76.             item: Handle;
  77.             box: Rect;
  78.     begin
  79.  
  80.         if theEvent.what = updateEvt then
  81.             begin
  82.                 BeginUpdate(theDialog);
  83.                 SetPort(theDialog);
  84.  
  85.                 DrawDialog(theDialog);
  86.  
  87. {Frame button}
  88.                 GetDialogItem(theDialog, ok, kind, item, box);
  89.                 InsetRect(box, -4, -4);
  90.                 PenSize(3, 3);
  91.                 FrameRoundRect(box, 15, 15);
  92.  
  93.                 StdFilter := false;
  94.  
  95.                 EndUpdate(theDialog);
  96.             end;
  97.  
  98.         if theEvent.what = keyDown then
  99.             begin
  100.                 theChar := Char(BitAnd(theEvent.message, charCodeMask));
  101.                 if ((BitAnd(theEvent.modifiers, cmdkey) <> 0) and (theChar = '.')) or (theChar = char(27)) then {cmd-. eller ESC}
  102. {if TestDItemEnable(theDialog, cancel) then}
  103.                     begin
  104.                         itemHit := cancel;
  105. {Måste jag highlighta till keyup?}
  106.  
  107.                         GetDialogItem(theDialog, cancel, kind, item, box);
  108.                         HiliteControl(ControlHandle(item), 1);
  109.  
  110.                         StdFilter := true;
  111.                         exit(StdFilter);
  112.                     end;
  113.                 if (theChar = char(13)) or (theChar = char(3)) then
  114. {if TestDItemEnable(theDialog, ok) then}
  115.                     begin
  116.                         itemHit := ok;
  117.                         GetDialogItem(theDialog, ok, kind, item, box);
  118.                         HiliteControl(ControlHandle(item), 1);
  119.                         StdFilter := true;
  120.                         exit(StdFilter);
  121.                     end;
  122.             end;
  123.         StdFilter := false;
  124.     end; {StdFilter}
  125.  
  126. { Ask for players name (at highscore) }
  127.     function AskHigh: str255;
  128.         var
  129.             dialog: DialogPtr;
  130.             oldPort: SATPort;
  131.             itemHit: integer;
  132.             itemHandle: Handle;
  133.             itemType, item: integer;
  134.             itemRect: Rect;
  135.             str: str255;
  136.     begin
  137.         SATGetPort(oldPort);
  138.         SATSetPortScreen;
  139.         dialog := GetNewDialog(kAskHighDlog, nil, WindowPtr(-1));
  140.         ShowWindow(dialog);
  141.         SelectWindow(dialog);
  142.         if gSAT.colorFlag then
  143.             SetGDevice(GetMainDevice);
  144.         SetPort(dialog);
  145.  
  146.         GetDialogItem(dialog, 3, itemType, itemHandle, itemRect);
  147.         SetDialogItemText(itemHandle, hs^^.lastPlayer);
  148.         SelectDialogItemText(dialog, 3, 0, 32767);
  149.         itemHit := -1;
  150.         while (itemHit <> 1) and (itemHit <> 2) do { 1=ok, 2=cancel }
  151.             ModalDialog(@StdFilter, itemHit);
  152.         if itemHit = 2 then
  153.             begin
  154.                 AskHigh := '';
  155.             end;
  156.         if itemHit = 1 then
  157.             begin
  158.                 GetDialogItem(dialog, 3, itemType, itemHandle, itemRect);
  159.                 GetDialogItemText(itemHandle, str);
  160.                 if Length(str) > kStringSize then
  161.                     str[0] := Char(kStringSize);
  162.                 hs^^.lastPlayer := str;
  163.                 AskHigh := str;
  164.             end;
  165.         DisposeDialog(dialog);
  166.         SATSetPort(oldPort);
  167.     end; {AskHigh}
  168.  
  169.  
  170.     procedure DrawHighScores (bounds: Rect; rankPos, namePos, datePos, levelPos: Integer; markLatest: Boolean);
  171. {bounds: Area in which to draw. (Current port!)}
  172. {rankPos, namePos, datePos, levelPos: Right edge of each subfield.}
  173. {markLatest: Draw latest in red?}
  174. {Note: The score is always drawn to the right!}
  175.         var
  176.             rankBox, nameBox, dateBox, levelBox, scoreBox: Rect;
  177.             info: FontInfo;
  178.             spacing, spillSpacing: Integer;
  179.             saveColor: RGBColor;
  180.             saveBWcolor: Longint;
  181.             i: Integer;
  182.             dateString: Str255;
  183.  
  184.         procedure RestoreColor;
  185.         begin
  186.             if gSAT.colorFlag then
  187.                 RGBForeColor(saveColor)
  188.             else
  189.                 ForeColor(saveBWcolor);
  190.         end; {RestoreColor}
  191.  
  192.         procedure ProperColor (index: Integer);
  193.         begin
  194.             if (index = gLastHigh) and markLatest then
  195.                 ForeColor(redColor)
  196.             else
  197.                 RestoreColor;
  198.         end; {ProperColor}
  199.  
  200.         function Max (a, b: integer): Integer;
  201.         begin
  202.             if a > b then
  203.                 Max := a
  204.             else
  205.                 Max := b;
  206.         end; {Max}
  207.  
  208.         function Min (a, b: integer): Integer;
  209.         begin
  210.             if a < b then
  211.                 Min := a
  212.             else
  213.                 Min := b;
  214.         end; {Min}
  215.  
  216.         procedure DrawStringRight (str: Str255; width: Integer);
  217.         begin
  218.             Move(width - StringWidth(str), 0);
  219.             DrawString(str);
  220.         end; {DrawStringRight}
  221.  
  222.         function MyNumToString (num: Longint): Str255;
  223.             var
  224.                 str: Str255;
  225.         begin
  226.             NumToString(num, str);
  227.             MyNumToString := str;
  228.         end; {MyNumToString}
  229.  
  230.         function RectWidth (r: Rect): integer;
  231.         begin
  232.             RectWidth := r.right - r.left;
  233.         end; {RectWidth}
  234.  
  235.         function RectHeight (r: Rect): integer;
  236.         begin
  237.             RectHeight := r.bottom - r.top;
  238.         end; {RectHeight}
  239.  
  240.     begin {DrawHighScores}
  241.         if not scoresInitialized then
  242.             InitScores(CurResFile);
  243.  
  244.         rankBox := bounds;
  245.         nameBox := bounds;
  246.         dateBox := bounds;
  247.         levelBox := bounds;
  248.         scoreBox := bounds;
  249.  
  250.         rankBox.right := rankPos;
  251.         nameBox.left := rankPos;
  252.         nameBox.right := Min(datePos, levelPos);
  253.  
  254.         dateBox.right := datePos;
  255. {dateBox.left := Min(namePos, levelPos);}
  256.         levelBox.right := levelPos;
  257. {levelBox.left := Min(namePos, datePos);}
  258.  
  259.         if datePos <= levelPos then
  260.             begin
  261.                 dateBox.left := namePos;
  262.                 levelBox.left := datePos;
  263.             end
  264.         else
  265.             begin
  266.                 dateBox.left := levelPos;
  267.                 levelBox.left := namePos;
  268.             end;
  269.  
  270.         scoreBox.left := Max(datePos, levelPos);
  271.  
  272.         nameBox.left := nameBox.left + kMargin;
  273.         dateBox.left := dateBox.left + kMargin;
  274.         levelBox.left := levelBox.left + kMargin;
  275.         scoreBox.left := scoreBox.left + kMargin;
  276.  
  277.         GetFontInfo(info);
  278.         spillSpacing := (RectHeight(bounds) - (info.ascent + info.descent) * kListLength) div (kListLength);
  279.         spacing := spillSpacing + info.ascent + info.descent;
  280.         spillSpacing := spillSpacing div 2;
  281.  
  282.         if gSAT.colorFlag then
  283.             GetForeColor(saveColor)
  284.         else
  285. {$IFC UNDEFINED THINK_PASCAL}
  286.             saveBWcolor := qd.thePort^.fgColor;
  287. {$ELSEC}
  288.         saveBWcolor := thePort^.fgColor;
  289. {$ENDC}
  290.  
  291. {Draw rank}
  292.         if RectWidth(rankBox) > 0 then
  293.             for i := 1 to kListLength do
  294.                 begin
  295.                     ProperColor(i);
  296.                     ClipRect(rankBox);
  297.                     MoveTo(rankBox.left, bounds.top + spillSpacing + info.ascent + spacing * (i - 1));
  298.                     DrawStringRight(MyNumToString(i), RectWidth(rankBox));
  299.                 end;
  300.  
  301. {Draw name}
  302.         if RectWidth(nameBox) > 0 then
  303.             for i := 1 to kListLength do
  304.                 begin
  305.                     ProperColor(i);
  306.                     ClipRect(nameBox);
  307.                     MoveTo(nameBox.left, bounds.top + spillSpacing + info.ascent + spacing * (i - 1));
  308.                     DrawString(hs^^.highPlayer[i]);
  309.                 end;
  310.  
  311. {Draw date}
  312.         if RectWidth(dateBox) > 0 then
  313.             for i := 1 to kListLength do
  314.                 begin
  315.                     ProperColor(i);
  316.                     ClipRect(dateBox);
  317.                     MoveTo(dateBox.left, bounds.top + spillSpacing + info.ascent + spacing * (i - 1));
  318.                     if hs^^.when[i] <> 0 then
  319.                         IUDateString(hs^^.when[i], shortDate, dateString)
  320.                     else
  321.                         dateString := '-';
  322.                     DrawStringRight(dateString, RectWidth(dateBox));
  323.                 end;
  324.  
  325. {Draw level}
  326.         if RectWidth(levelBox) > 0 then
  327.             for i := 1 to kListLength do
  328.                 begin
  329.                     ProperColor(i);
  330.                     ClipRect(levelBox);
  331.                     MoveTo(levelBox.left, bounds.top + spillSpacing + info.ascent + spacing * (i - 1));
  332.                     DrawStringRight(MyNumToString(hs^^.level[i]), RectWidth(levelBox));
  333.                 end;
  334.  
  335. {Draw score}
  336.         if RectWidth(scoreBox) > 0 then
  337.             for i := 1 to kListLength do
  338.                 begin
  339.                     ProperColor(i);
  340.                     ClipRect(scoreBox);
  341.                     MoveTo(scoreBox.left, bounds.top + spillSpacing + info.ascent + spacing * (i - 1));
  342.                     DrawStringRight(MyNumToString(hs^^.highScore[i]), RectWidth(scoreBox));
  343.                 end;
  344.         RestoreColor;
  345. {$IFC UNDEFINED THINK_PASCAL}
  346.         ClipRect(qd.thePort^.portRect); {Set to a reasonable cliprect!}
  347. {$ELSEC}
  348.         ClipRect(thePort^.portRect); {Set to a reasonable cliprect!}
  349. {$ENDC}
  350.     end; {DrawHighScores}
  351.  
  352. {*** TestNewHigh ***}
  353. {Call this on game over!}
  354. {It checks if the current score is a new high score, and if so,}
  355. {fires up a modal dialog and ask for the player's name.}
  356. {It returns true if the high score list was altered, to signal that you}
  357. {should display the high score list.}
  358.     function TestNewHigh (level: Integer): Boolean;
  359.         var
  360.             num, len: integer;
  361.             name, s: str255;
  362.     begin
  363.         if not scoresInitialized then
  364.             InitScores(CurResFile);
  365.  
  366.         TestNewHigh := false;
  367.         gLastHigh := 0;
  368.         if gScore > hs^^.highScore[kListLength] then
  369.             begin
  370.                 num := kListLength;
  371.                 name := AskHigh;
  372.                 if name = '' then { alt length(name) = 0 }
  373.                     Exit(TestNewHigh);
  374.  
  375.                 TestNewHigh := true;
  376.  
  377.                 if length(name) > kStringSize then
  378.                     name := copy(name, 1, kStringSize);
  379.  
  380.                 while (hs^^.highScore[num - 1] < gScore) and (num > 1) do
  381.                     begin
  382.                         hs^^.highScore[num] := hs^^.highScore[num - 1];
  383.                         hs^^.highPlayer[num] := hs^^.highPlayer[num - 1];
  384.                         hs^^.level[num] := hs^^.level[num - 1];
  385.                         hs^^.when[num] := hs^^.when[num - 1];
  386.                         num := num - 1;
  387.                     end;
  388.                 gLastHigh := num; {Remember last high for the highscore display}
  389.                 hs^^.highScore[num] := gScore;
  390.                 hs^^.highPlayer[num] := name;
  391.                 hs^^.level[num] := level;
  392.                 GetDateTime(hs^^.when[num]);
  393.                 ChangedResource(Handle(hs));
  394.             end;
  395.     end; {TestNewHigh}
  396.  
  397.  
  398. {IsNewHigh and SetNewHigh should be used if you don't want the default modal dialog, but rather}
  399. {get the player name some other way.}
  400.  
  401. {Checks if the current score is high enough for the high score list.}
  402.     function IsNewHigh: Boolean;
  403.         var
  404.             num, len: integer;
  405.             name, s: str255;
  406.     begin
  407.         if not scoresInitialized then
  408.             InitScores(CurResFile);
  409.  
  410.         IsNewHigh := gScore > hs^^.highScore[kListLength];
  411.     end; {IsNewHigh}
  412.  
  413.  
  414. { Call this on game over! }
  415.     procedure SetNewHigh (name: Str255; level: Integer);
  416.         var
  417.             num, len: integer;
  418.             s: str255;
  419.     begin
  420.         if not scoresInitialized then
  421.             InitScores(CurResFile);
  422.  
  423.         gLastHigh := 0;
  424.         if gScore > hs^^.highScore[kListLength] then
  425.             begin
  426.                 num := kListLength;
  427.                 if name = '' then { alt length(name) = 0 }
  428.                     Exit(SetNewHigh);
  429.  
  430.                 if length(name) > kStringSize then
  431.                     name := copy(name, 1, kStringSize);
  432.  
  433.                 while (hs^^.highScore[num - 1] < gScore) and (num > 1) do
  434.                     begin
  435.                         hs^^.highScore[num] := hs^^.highScore[num - 1];
  436.                         hs^^.highPlayer[num] := hs^^.highPlayer[num - 1];
  437.                         hs^^.level[num] := hs^^.level[num - 1];
  438.                         hs^^.when[num] := hs^^.when[num - 1];
  439.                         num := num - 1;
  440.                     end;
  441.                 gLastHigh := num; {Remember last high for the highscore display}
  442.                 hs^^.highScore[num] := gScore;
  443.                 hs^^.highPlayer[num] := name;
  444.                 hs^^.level[num] := level;
  445.                 GetDateTime(hs^^.when[num]);
  446.                 ChangedResource(Handle(hs));
  447.             end;
  448.     end; {TestNewHigh}
  449.  
  450.  
  451.  
  452.  
  453.  
  454.  
  455.     procedure ZeroScore;
  456.     begin
  457.         if not scoresInitialized then
  458.             InitScores(CurResFile);
  459.  
  460.         gScore := 0;
  461.         gLastHigh := -1;
  462.         gNextLimit := kFirstLimit; {Nästa gräns för nytt liv!}
  463.     end;
  464.  
  465.     procedure EraseHighScores (ask: Boolean);
  466.         var
  467.             doIt: Boolean;
  468.             i: Integer;
  469.     begin
  470.         if not scoresInitialized then
  471.             InitScores(CurResFile);
  472.  
  473.         if ask then
  474.             doIt := SATQuestionStr('Are you sure you want to erase the high scores?')
  475.         else
  476.             doIt := true;
  477.         if doIt then
  478.             begin
  479.                 for i := 1 to kListLength do
  480.                     begin
  481.                         hs^^.highScore[i] := 0;
  482.                         hs^^.highPlayer[i] := 'Noone';
  483.                         hs^^.level[i] := 0;
  484.                         hs^^.when[i] := 0;
  485.                     end;
  486.                 ChangedResource(handle(hs));
  487.                 gLastHigh := -1;
  488.             end;
  489.     end; {EraseHighScores}
  490.  
  491.     procedure InitScores (prefFile: Integer);
  492.         var
  493.             saveResFile: Integer;
  494.     begin
  495.         gPrefFile := prefFile;
  496.         saveResFile := CurResFile;
  497.         UseResFile(prefFile);
  498.  
  499.         scoresInitialized := true;
  500.  
  501.         gLastHigh := -1; {no "last"}
  502.  
  503.         hs := hsHnd(GetResource('Bäst', 0));        {"Bäst" is "best" in swedish, in case you wonder…}
  504.         if hs = nil then
  505.             begin
  506.                 hs := hsHnd(NewHandle(Sizeof(hsRec)));
  507.                 CheckNoMem(Ptr(hs));
  508.                 EraseHighScores(false);
  509.                 AddResource(handle(hs), 'Bäst', 0, 'High scores');
  510.             end
  511.         else if GetHandleSize(Handle(hs)) < sizeof(hsRec) then
  512.             SetHandleSize(Handle(hs), sizeof(hsRec));
  513.  
  514.         ZeroScore;
  515.  
  516.         UseResFile(saveResFile);
  517.     end; {InitScores}
  518.  
  519.  
  520.     procedure AddScore (amount: Longint);
  521.     begin
  522.         if not scoresInitialized then
  523.             InitScores(CurResFile);
  524.  
  525.         gScore := gScore + amount;
  526.  
  527.         if gNextLimit > 0 then
  528.             if gScore >= gNextLimit then
  529.                 DoLimit;
  530.  
  531.         DrawScore(gScore);
  532.     end; {AddScore}
  533.  
  534.     procedure AddScoreImmediate (amount: Longint);
  535.     begin
  536.         if not scoresInitialized then
  537.             InitScores(CurResFile);
  538.  
  539.         gScore := gScore + amount;
  540.  
  541.         if gNextLimit > 0 then
  542.             if gScore >= gNextLimit then
  543.                 DoLimit;
  544.  
  545.         DrawScoreImmediate(gScore);
  546.     end; {AddScoreImmediate}
  547.  
  548. end.